/**
 * 泛型约束
 * 一种方法使返回值的类型与传入参数的类型是相同的
 */

// 1 简单
// function identity<T>(arg: T): T {
//     return arg;
// }
// let output = identity<string>("myString");
// console.log(output); // myString
//**********************

// 2 使用泛型变量

// function loggingIdentity<T>(arg: T): T {  // 没有地方指明arg具有这个属
//     console.log(arg.length);  
//     return arg;
// }

// 操作T类型的数组
// function loggingIdentity<T>(arg: T[]): T[] {
//     console.log(arg.length);  // Array has a .length, so no more error
//     return arg;
// }

// 同上
// function loggingIdentity<T>(arg: Array<T>): Array<T> {
//     console.log(arg.length);  // Array has a .length, so no more error
//     return arg;
// }

// 3 泛型类型
// function identity<T>(arg: T): T {
//     return arg;
// }

// let myIdentity: <U>(arg: U) => U = identity;

// 调用签名的对象字面量来定义泛型函数
// function identity<T>(arg: T): T {
//     return arg;
// }

// let myIdentity: {<T>(arg: T): T} = identity;

// 对象字面量拿出来做为一个接口    泛型接口
// interface GenericIdentityFn {
//     <T>(arg: T): T;
// }

// function identity<T>(arg: T): T {
//     return arg;
// }

// let myIdentity: GenericIdentityFn = identity;

// 泛型参数当作整个接口的一个参数
interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}
// 传入一个类型参数来指定泛型类型 number 锁定了之后代码里使用的类型
let myIdentity: GenericIdentityFn<number> = identity;

// 4 泛型类
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

// let myGenericNumber = new GenericNumber<number>();
// myGenericNumber.zeroValue = 0;
// myGenericNumber.add = function(x, y) { return x + y; };

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
// #### 类有两部分：静态部分和实例部分。 泛型类指的是实例部分的类型，所以类的静态属性不能使用这个泛型类型。

// 5 泛型约束
// 接口来描述约束条件
// 使用这个接口和extends关键字还实现约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

// 我们需要传入符合约束类型的值，必须包含必须的属性：
loggingIdentity({length: 10, value: 3});


// 在泛型约束中使用类型参数

/**
 *  把一个对象的属性拷贝到另一个对象
  * 确保没有不小心地把额外的属性从源对象拷贝到目标对象
 */
// function copyFields<T extends U, U>(target: T, source: U): T {
//     for (let id in source) {
//         target[id] = source[id];
//     }
//     return target;
// }

// let x = { a: 1, b: 2, c: 3, d: 4 };

// copyFields(x, { b: 10, d: 20 }); // okay
// copyFields(x, { Q: 90 });  // error: property 'Q' isn't declared in 'x'.

// 6 在泛型里使用类类型
function create<T>(c: {new(): T; }): T {
    return new c();
}


// 使用原型属性推断并约束构造函数与类实例的关系。
// tood 
class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function findKeeper<A extends Animal, K> (a: {new(): A;
    prototype: {keeper: K}}): K {

    return a.prototype.keeper;
}

// findKeeper(Lion).nametag;  // typechecks!



